﻿// #define DEBUG_MSG

namespace JoinBox.WPF.LabelBarStandard.View;

/// <summary>
/// 嵌入WPF
/// </summary>
public class Anchor : IDisposable
{
    #region 成员
    /// <summary>
    /// 挤出空间成功的标记
    /// </summary>
    bool _reservedFlag = false;
    /// <summary>
    /// 文档的WPF控件
    /// </summary>
    readonly WPFFrame _WPFFrame = new(AutoGo.AcapMainSyncEx.Handle);
    /// <summary>
    /// 键盘钩子
    /// </summary>
    KeyboardHook? _KeyboardHook;
    #endregion

    /// <summary>
    /// 嵌入WPF
    /// </summary>
    public Anchor()
    {
        AcapMainWndProc();
        AcapMidWndProc();
        SetKeyboardHook();
        CreateWPFThreadAndEmbed();
        SetWindowPosToMid();
    }

    /// <summary>
    /// 子类化拦截消息_文档管理器<br/>
    /// 挤出工具条的空间,令其他空间缩小
    /// </summary>
    void AcapMidWndProc()
    {
#if _delayed
        DateTime? bak = null;
        TimeSpan dete = TimeSpan.FromMilliseconds(200);
#endif
        AutoGo.AcapMidSyncEx.WndProc(m => {
            var msg = (WM)m.Msg;

            // 过滤杂讯
            if (msg == (WM.WM_SETCURSOR | WM.WM_MBUTTONDBLCLK))
                return true;

            try
            {
                if (!_reservedFlag && msg == WM.WM_NCCALCSIZE && m.WParam != IntPtr.Zero)
                {
                    AnchorHelper.SqueezeSize(m.LParam);
                    _reservedFlag = true;
                }

#if !_delayed
                /// cad08没有它则最大化/最小化没有刷新,
                /// 并且cad08是不能识别 <see cref="WM.WM_MOVE"/> 的
                if (msg == WM.WM_SIZE)
                    RefreshPos();
#endif

#if _delayed
                if (msg == WM.WM_MOVE)
                {
                    bak ??= DateTime.Now;
                    Debug.WriteLine("a2__WM_MOVE__" + DateTime.Now);
                }

                // 延时任务
                if (bak != null && DateTime.Now - bak > dete)
                {
                    Debug.WriteLine("a3__Name:" + Thread.CurrentThread.Name);
                    Debug.WriteLine("a3__Id:" + Thread.CurrentThread.ManagedThreadId);
                    Debug.WriteLine("a3__DateTime.Now" + DateTime.Now);
                    Debug.WriteLine("a3__DateTime.Now - bak:" + (DateTime.Now - bak));

                    bak = null; // 不可以放后面,因为后面又触发消息循环了
                    RefreshPos();
                }
#endif
            }
            catch (Exception e) // cad崩溃的时候会触发
            {
                AutoGo.Printl(e);
                Debugger.Break();
            }
            return true;
        });
    }

    // 这里拦截一下两种发送方式
    // SendMessage(Acap.MainWindow.Handle, WM_COMMAND, (IntPtr)18, IntPtr.Zero);   // qnew
    // SendMessage(Acap.MainWindow.Handle, WM_COMMAND, (IntPtr)57600, IntPtr.Zero);// new
    readonly IntPtr _new = (IntPtr)0xe100;      // 对应cad命令,0xe100==57600
    readonly IntPtr _qnew = (IntPtr)0x12;       // 对应cad命令,0x12==18,工具条也是它
    readonly IntPtr _chongZhi = (IntPtr)0x1e100;// 重置之后 ctrl+n 取得的
    // 勇芳spy发送的时候只能用 十进制 不能用二进制

    /// <summary>
    /// 子类化拦截消息_cad程序
    /// </summary>
    void AcapMainWndProc()
    {
        //AutoGo.AcapMainSyncEx2.WndProc(m => {
        //    Debug.WriteLine(m);
        //    return true;
        //});

        //WM.WM_POPMESSAGESTRING
        AutoGo.AcapMainSyncEx.WndProc(m => {
            var msg = (WM)m.Msg;
            if (msg != WM.WM_COMMAND) //勇芳spy要输入十进制 273
                return true;

            try
            {
#if true2
                // 鼠标按着文档窗口移动时候触发 WM.WM_MOUSEACTIVATE
                // https://social.msdn.microsoft.com/Forums/en-US/572cf1ac-c55f-47cb-b41c-bbc0d7fd23b8/wndproc-detect-title-bar-clicked

                // 这里是可用的,但是拖动文档窗口会频繁发生,
                // 拦截每个文档栏的拖动又会产生消息通知滞后,所以不想做了
                // 拖动窗口/最大化/还原等尺寸改变时候触发,触发比WM_SIZE多
                // 调整cad文档尺寸
                // 这里只是拦截cad主窗口改变大小
                if (msg == WM.WM_WINDOWPOSCHANGED)
                   AnchorHelper.ReWindowSize();
#endif
                if (m.WParam == _new || m.WParam == _qnew || m.WParam == _chongZhi)
                {
                    // 之后是阻止cad接收win32的ctrl+n消息,需要键盘钩子hook
                    // 从而防止发送消息引起的致命错误
                    DocWindow.Post_Qnew();
                    return false;
                }
            }
            catch (Exception e)// cad崩溃的时候会触发
            {
                AutoGo.Printl(e);
                Debugger.Break();
            }
            return true;
        });
    }

    /// <summary>
    /// 创建WPF线程
    /// </summary>
    void CreateWPFThreadAndEmbed()
    {
        if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
            Thread.CurrentThread.Name = $"AcadMain_Thread";

        // 创建一个新线程,等待cad界面完成,然后进行 WPF 嵌入
        AutoGo.AcapMainAwait(false, () => {
            void task()
            {
                AutoGo.Printl($"**** {nameof(DocWindow)}嵌入中....");

                var docWindow = CreateClass.GetService<DocWindow>();
                if (docWindow == null)
                {
                    AutoGo.Printl($"**** {nameof(DocWindow)}无法创建,结束\n\r");
                    return;
                }
                var wpfName = $"WPFThread_{docWindow}";
                if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
                    Thread.CurrentThread.Name = wpfName;

                _WPFFrame.Build(docWindow);
                _WPFFrame.Name = wpfName;

                AnchorHelper.DragDrop(docWindow);
                RefreshPos();

                AutoGo.Printl($"**** {nameof(DocWindow)}嵌入完成!\n\r");
            }

            if (DocWindow.DispatcherRunFlag)
            {
                // net35的利用此线程作为WPF线程
                task();
                System.Windows.Threading.Dispatcher.Run();
            }
            else
            {
                // 高版本WPF在主线程上面运行
                AutoGo.Post(task);
            }
        }, () => {
            if (!_reservedFlag)
            {
                AutoGo.AcapMidSyncEx.Post(SetWindowPosToMid);
                return false;
            }
            return true;
        });
    }




    /// <summary>
    /// 键盘钩子
    /// </summary>
    void SetKeyboardHook()
    {
        _KeyboardHook = new KeyboardHook();
        _KeyboardHook.KeyDownEvent += (sender, e) => {
            if (_WPFFrame.WPFDispatcher == null)
                return;

            // 进程号拦截
            // GetWindowThreadProcessId(GetForegroundWindow(), out uint winId);
            // if (Process.GetCurrentProcess().Id == winId)

            // 窗体句柄拦截
            // ctrl+n替换,减少原生命令的致命错误
            if (e.KeyCode == Keys.N
                && System.Windows.Forms.Control.ModifierKeys == Keys.Control
                && GetForegroundWindow() == AutoGo.AcapMainSyncEx.Handle)
            {
                DocWindow.Post_Qnew();
                ((KeyboardHook)sender).IsHookBreak = true;
            };
        };
    }

    /// <summary>
    /// 刷新位置<br/>
    /// 不加这个会文档栏界面被绘图区挡住
    /// </summary>
    public void RefreshPos()
    {
        // 暂停刷新,用来移除多文档栏
        if (IsDisposed)
            return;

        (int x, int y, int w, int h) = AnchorHelper.GetSizeInfo(_WPFFrame.Handle);
        _WPFFrame.SetRect(x, y, w, h);
        _WPFFrame.SetWindowPos();
    }

    /// <summary>
    /// 触发: <see cref="WM.WM_NCCALCSIZE"/><br/>
    /// 0x01 注册表加载,会自动触发<br/>
    /// 0x02 netload加载,则执行的时候已经加载了Mid窗口,因此不会触发,需要调用本函数<br/>
    /// 0x03 若鼠标在cad绘图区空选一下(或者移动出窗口外)才触发,需要调用本函数<br/>
    /// 0x04 若仍然不能就重启电脑再试<br/>
    /// 因为需要窗口的Rect信息<br/>
    /// 所以不能自己进行:<br/>
    /// <![CDATA[
    ///    SendMessage(AutoGo.AcapMidSyncEx.Handle,
    ///                WM.WM_NCCALCSIZE,
    ///                (IntPtr)0x01,
    ///                rect/*NCCALCSIZE_PARAMS*/)
    /// ]]><br/>
    /// </summary>
    public static void SetWindowPosToMid()
    {
        WindowsAPI.SetWindowPos(AutoGo.AcapMidSyncEx.Handle);
    }

    #region IDisposable接口相关函数
    public bool IsDisposed { get; private set; } = false;

    /// <summary>
    /// 手动调用释放
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// 析构函数调用释放
    /// </summary>
    ~Anchor()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        // 不重复释放
        if (IsDisposed) return;
        IsDisposed = true;

        // 卸载嵌入
        _WPFFrame.Dispose();

        // 卸载钩子
        _KeyboardHook?.Dispose();
        _KeyboardHook = null;

        // 排序窗口,触发刷新
        SetWindowPosToMid();
    }
    #endregion
}